package com.example.rabbitmqdemo.cacheTask;

import cn.hutool.cache.CacheListener;
import cn.hutool.core.date.DateUtil;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.StampedLock;

@Slf4j
public class FailCallRecordCacheHandle {

    // 乐观锁
    private final StampedLock lock = new StampedLock();

    private volatile boolean isSchedule = false;

    private CacheListener<String, List<String>> listener;

    private FailCallRecordCacheHandle() {
    }

    /**
     * 通过工厂方法获取势力
     */
    public static class Factory {
        private static FailCallRecordCacheHandle handle = new FailCallRecordCacheHandle();

        public static FailCallRecordCacheHandle create() {
            return handle;
        }
    }

    public void put(String key, String value) {
        this.put(key, value, 60);
    }

    /**
     * 对缓存中添加数据
     *
     * @param key
     * @param value
     */
    public void put(String key, String value, int ttl) {
        String key_time = getKey(key);
        final long stamp = lock.writeLock();
        try {
            Map<String, CacheObj<String, List<String>>> cacheMap = FailCallRecordConfig.INSTANCE.cacheMap();
            CacheObj<String, List<String>> cacheObj = Optional.ofNullable(cacheMap.get(key_time)).orElseGet(() -> {
                return new CacheObj<>(key, new ArrayList<>(), ttl);
            });
            cacheObj.getValue().add(value);
            cacheMap.put(key_time, cacheObj);
            // 当第一次添加数据的时候才 进行 定期任务开启
            if (!isSchedule) {
                FailCallRecordConfig.INSTANCE.pruneTimer().scheduleAtFixedRate(this::prune, ttl, ttl, TimeUnit.SECONDS);
                isSchedule = true;
            }
        } finally {
            lock.unlockWrite(stamp);
        }
    }

    /**
     * 删除缓存中过期的数据
     */
    public final void prune() {
        final long stamp = lock.writeLock();
        try {
            Map<String, CacheObj<String, List<String>>> cacheMap = FailCallRecordConfig.INSTANCE.cacheMap();
            Iterator<CacheObj<String, List<String>>> iterator = cacheMap.values().iterator();
            while (iterator.hasNext()) {
                CacheObj<String, List<String>> next = iterator.next();
                if (next.isExpired()) {
                    iterator.remove();
                    this.onRemove(next.getKey(), next.getValue());
                }
            }

        } finally {
            lock.unlockWrite(stamp);
        }
    }

    public void onRemove(String key, List<String> value) {
        final CacheListener<String, List<String>> listener = this.listener;
        if (listener != null) {
            FailCallRecordConfig.INSTANCE.executorListener().execute(() -> {
                listener.onRemove(key, value);
            });
        }
    }

    public void setListener(CacheListener<String, List<String>> listener) {
        this.listener = listener;
    }

    /**
     * 根据时间对key进行拼装，把同一分钟的key进行汇聚
     *
     * @param key
     * @return
     */
    private String getKey(String key) {
        LocalDateTime now = LocalDateTime.now();
        StringBuilder result = new StringBuilder(key);
        result.append("_");
        result.append(DateUtil.format(now, "yyyyMMddHHmm"));
        return result.toString();
    }
}
